package com.itjeffrey.autocode.util;

import com.baomidou.mybatisplus.extension.api.Assert;
import com.baomidou.mybatisplus.extension.enums.ApiErrorCode;
import com.itjeffrey.autocode.bo.DateDiff;
import com.itjeffrey.autocode.constant.SysConstant;
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.core.Local;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.*;

/**
 * 日期工具类
 *
 * @From: Jeffrey
 * @Date: 2020/12/22
 */
@Slf4j
public class DateUtil {

    public static final String PATTERN_YYYYMMDD = "yyyyMMdd";

    public static final String PATTERN_YYYY_MM_DD = "yyyy-MM-dd";

    public static final String PATTERN_HH_MM_SS = "HH:mm:ss";

    public static final String PATTERN_HH_MM_SS_SSS = "HH:mm:ss.SSS";

    public static final String PATTERN_YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    public static final String PATTERN_YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS";

    public enum LocalEnum {
        LocalDate, LocalTime, LocalDateTime
    }

    /**
     * 计算出指定日期相隔当前系统日期有多少年
     *
     * @param date
     * @return 指定日期距离现在的年数
     */
    public static int dateToAge(Date date) {
        Calendar nowCal = Calendar.getInstance();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return nowCal.get(Calendar.YEAR) - calendar.get(Calendar.YEAR);
    }

    /**
     * 年龄转出生日期
     *
     * @param age
     * @return
     */
    public static Date ageToDate(Integer age) {
        Assert.gt(age, 0, ApiErrorCode.FAILED);
        Assert.notNull(ApiErrorCode.FAILED, age);
        Calendar instance = Calendar.getInstance();
        instance.set(Calendar.YEAR, instance.get(Calendar.YEAR) - age);
        return instance.getTime();
    }

    /**
     * 格式化日期为年月日时分秒的时间字符串 yyyy-MM-dd HH:mm:ss.SSS
     *
     * @param date
     * @return
     */
    public static String sdfDate(Date date) {
        if (date == null) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_YYYY_MM_DD_HH_MM_SS_SSS);
        return sdf.format(date);
    }

    /**
     * 格式化日期为年月日时分秒的时间字符串 yyyy-MM-dd HH:mm:ss
     *
     * @param date
     * @return
     */
    public static String sdfDateTime(Date date) {
        if (date == null) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_YYYY_MM_DD_HH_MM_SS);
        return sdf.format(date);
    }

    /**
     * 获取去除时分秒的日期
     *
     * @param date
     * @return
     */
    public static Date eraseTime(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.set(Calendar.HOUR_OF_DAY, 0);
        instance.set(Calendar.MINUTE, 0);
        instance.set(Calendar.SECOND, 0);
        instance.set(Calendar.MILLISECOND, 0);
        return instance.getTime();
    }

    /**
     * 解析日期字符串
     * yyyy-MM-dd HH:mm:ss.SSS
     *
     * @param dateStr
     * @return
     */
    public static Date parseDateStr(String dateStr) {
        if (StringUtils.isBlank(dateStr)) {
            return null;
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(PATTERN_YYYY_MM_DD_HH_MM_SS_SSS);
        Date date = null;
        try {
            date = simpleDateFormat.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 获取当前时间Timestamp
     *
     * @return
     */
    public static Timestamp getCurTime() {
        return new Timestamp(System.currentTimeMillis());
    }

    /**
     * 格式化日期为年月日的时间字符串 yyyy-MM-dd
     *
     * @param date
     * @return
     */
    public static String sdfDateNoTime(Date date) {
        if (date == null) {
            return null;
        }
        return new SimpleDateFormat(PATTERN_YYYY_MM_DD).format(date);
    }

    /**
     * 格式化LocalDatetime，默认格式yyyyMMdd
     *
     * @return
     */
    public static String formatLocalDatetimeOnYYYYMMDD(LocalDateTime localDateTime) {
        return formatLocalDatetime(localDateTime, PATTERN_YYYYMMDD);
    }

    /**
     * 格式化LocalDatetime，默认格式yyyy-MM-dd
     *
     * @return
     */
    public static String formatLocalDatetimeWithDate(LocalDateTime localDateTime) {
        return formatLocalDatetime(localDateTime, PATTERN_YYYY_MM_DD);
    }

    /**
     * 格式化LocalDatetime，默认格式HH:mm:ss
     *
     * @return
     */
    public static String formatLocalDatetimeWithTime(LocalDateTime localDateTime) {
        return formatLocalDatetime(localDateTime, PATTERN_HH_MM_SS);
    }

    /**
     * 格式化LocalDatetime，默认格式HH:mm:ss.SSS
     *
     * @return
     */
    public static String formatLocalDatetimeWithoutDate(LocalDateTime localDateTime) {
        return formatLocalDatetime(localDateTime, PATTERN_HH_MM_SS_SSS);
    }

    /**
     * 格式化LocalDatetime，默认格式yyyy-MM-dd HH:mm:ss
     *
     * @return
     */
    public static String formatLocalDatetimeWithoutMinus(LocalDateTime localDateTime) {
        return formatLocalDatetime(localDateTime, PATTERN_YYYY_MM_DD_HH_MM_SS);
    }

    /**
     * 格式化LocalDatetime，默认格式yyyy-MM-dd HH:mm:ss.SSS
     *
     * @return
     */
    public static String formatLocalDatetimeWithMinus(LocalDateTime localDateTime) {
        return formatLocalDatetime(localDateTime, PATTERN_YYYY_MM_DD_HH_MM_SS_SSS);
    }

    /**
     * 格式化LocalDatetime，指定日期时间格式pattern
     *
     * @return
     */
    public static String formatLocalDatetime(LocalDateTime localDateTime, String pattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        return localDateTime.format(dateTimeFormatter);
    }

    /**
     * 解析LocalDatetime，DateTimeFormatter ISO_LOCAL_DATE_TIME
     *
     * @param var
     * @return
     */
    public static LocalDateTime parseISOLocalDatetime(String var) {
        return parseLocalDatetime(var, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }

    /**
     * 解析LocalDateTime格式的字符串
     *
     * @param var
     * @return
     */
    public static LocalDateTime parseLocalDateTime(String var) {
        return parse(var, LocalEnum.LocalDateTime);
    }

    /**
     * 解析LocalTime格式的字符串
     *
     * @param var
     * @return
     */
    public static LocalTime parseLocalTime(String var) {
        return parse(var, LocalEnum.LocalTime);
    }

    /**
     * 解析LocalDate格式的字符串
     *
     * @param var
     * @return
     */
    public static LocalDate parseLocalDate(String var) {
        return parse(var, LocalEnum.LocalDate);
    }

    /**
     * 解析日期时间
     *
     * @param var 需要解析的字符串
     * @param t   日期时间类型, LocalDate, LocalTime, LocalDateTime
     * @return
     */
    public static <T> T parse(String var, LocalEnum t) {
        log.info("DateUtil parse is Starting===var:[{}]===t:[{}]", var, t);
        if (StringUtils.isBlank(var)) {
            return null;
        }
        //反射获取DateTimeFormatter所有静态属性
        Class<DateTimeFormatter> dateTimeFormatterClass = DateTimeFormatter.class;
        Map<String, DateTimeFormatter> dateTimeFormatters = new ConcurrentHashMap<>();
        Field[] fields = dateTimeFormatterClass.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                if (field.getType().toString().endsWith(SysConstant.FULL_CLASS_NAME_DATETIMEFORMATTER)
                        && Modifier.isStatic(field.getModifiers())) {
                    dateTimeFormatters.put(field.getName(), (DateTimeFormatter) field.get(DateTimeFormatter.class));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Set<Map.Entry<String, DateTimeFormatter>> entries = dateTimeFormatters.entrySet();
        //遍历，解析var
        //报错就下一个formatter解析var，成功则返回结果
        T temporal = null;
        Map<String, Object> map = new ConcurrentHashMap<>();
        map.put("flag", false);
        map.put("key", null);
        for (Map.Entry<String, DateTimeFormatter> entry : entries) {
            String key = entry.getKey();
            DateTimeFormatter formatter = entry.getValue();
            try {
                switch (t) {
                    case LocalDate:
                        temporal = (T) LocalDate.parse(var, formatter);
                        map.put("flag", true);
                        map.put("key", key);
                        break;
                    case LocalTime:
                        temporal = (T) LocalTime.parse(var, formatter);
                        map.put("flag", true);
                        map.put("key", key);
                        break;
                    case LocalDateTime:
                        temporal = (T) LocalDateTime.parse(var, formatter);
                        map.put("flag", true);
                        map.put("key", key);
                        break;
                    default:
                        log.warn("参数[{}]类型无效！", t);
                        return null;
                }
            } catch (DateTimeParseException e) {
                continue;
            } catch (Exception e) {
                log.error("解析失败！", e);
            }
        }
        if (temporal == null) {
            log.error("[{}]格式解析失败！", var);
        }
        String key = null;
        if ((boolean) map.get("flag")) {
            key = (String) map.get("key");
        }
        log.info("DateUtil parse End===var:[{}]===t:[{}]===dateTimeFormatter:[{}]", var, t, key);
        return temporal;
    }

    /**
     * 解析LocalDatetime
     *
     * @param var
     * @param dateTimeFormatter
     * @return
     */
    public static LocalDateTime parseLocalDatetime(String var, DateTimeFormatter dateTimeFormatter) {
        return LocalDateTime.parse(var, dateTimeFormatter);
    }

    /**
     * 解析LocalDate
     *
     * @param var
     * @param dateTimeFormatter
     * @return
     */
    public static LocalDate parseLocalDate(String var, DateTimeFormatter dateTimeFormatter) {
        return LocalDate.parse(var, dateTimeFormatter);
    }

    /**
     * 解析LocalTime
     *
     * @param var
     * @param dateTimeFormatter
     * @return
     */
    public static LocalTime parseLocalTime(String var, DateTimeFormatter dateTimeFormatter) {
        return LocalTime.parse(var, dateTimeFormatter);
    }

    /**
     * LocalDateTime转毫秒时间戳
     *
     * @param localDateTime LocalDateTime
     * @return 时间戳
     */
    public static Long localDateTimeToTimestamp(LocalDateTime localDateTime) {
        try {
            ZoneId zoneId = ZoneId.systemDefault();
            Instant instant = localDateTime.atZone(zoneId).toInstant();
            return instant.toEpochMilli();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 时间戳转LocalDateTime
     *
     * @param timestamp 时间戳
     * @return LocalDateTime
     */
    public static LocalDateTime timestampToLocalDateTime(long timestamp) {
        try {
            Instant instant = Instant.ofEpochMilli(timestamp);
            ZoneId zone = ZoneId.systemDefault();
            return LocalDateTime.ofInstant(instant, zone);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Date转LocalDateTime
     *
     * @param date Date
     * @return LocalDateTime
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        try {
            Instant instant = date.toInstant();
            ZoneId zoneId = ZoneId.systemDefault();
            return instant.atZone(zoneId).toLocalDateTime();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * LocalDateTime转Date
     *
     * @param localDateTime LocalDateTime
     * @return Date
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        try {
            ZoneId zoneId = ZoneId.systemDefault();
            ZonedDateTime zdt = localDateTime.atZone(zoneId);
            return Date.from(zdt.toInstant());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 计算两个date的时间差
     * @param oldDate
     * @param nowDate
     * @return
     */
    public static DateDiff getDateDiff(Date oldDate, Date nowDate){
        long day_diff = 1000 * 24 * 60 * 60;
        long hour_diff = 1000 * 60 * 60;
        long min_diff = 1000 * 60;
        long sec_diff = 1000;
        long diff = nowDate.getTime() - oldDate.getTime();
        DateDiff dateDiff = new DateDiff();
        dateDiff.setDiff(diff);
        dateDiff.setDay(diff/day_diff);
        dateDiff.setHour(diff%day_diff/hour_diff);
        dateDiff.setMin(diff%day_diff%hour_diff/min_diff);
        dateDiff.setSec(diff%day_diff%hour_diff%min_diff/sec_diff);
        return dateDiff;
    }

    /**
     * date转时差，+8hour
     * @param date
     * @return
     */
    public static Date toChineseDate(Date date){
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR_OF_DAY, 8);
        return instance.getTime();
    }

}
